﻿using System;
using System.Windows.Forms;
using LDF;
using TOOLKIT;
using TOOLKIT.Utils;
using CsGL.Basecode;
using CsGL.OpenGL;
using CsGL.Util;

namespace DefaultProject
{
    /// <summary>
    /// The different type of event we are handling
    /// </summary>
    public enum eventType
    { 
        none,
        press,
        drag,
        release
    };
    /// <summary>
    /// This class extends the CsGL Model class which give us 
    /// all the capabilities to handle an Opengl window and mouse inputs...
    /// </summary>
    public class MainWindow : Model
    {
        #region Attributes
        /// <summary>
        /// The wisth of the window
        /// </summary>
        protected uint width;
        /// <summary>
        /// The height of the window
        /// </summary>
        protected uint height;
        /// <summary>
        /// true if running a fullScreen Mode
        /// otherwise false
        /// </summary>
        protected bool fullScreen;
        /// <summary>
        /// The manager of all component
        /// </summary>
        protected ApplicationManager manager;

        /// <summary>
        /// Press event identification
        /// </summary>
        protected uint pressType;
        /// <summary>
        /// Drag Type identification
        /// </summary>
        protected uint dragType;
        /// <summary>
        /// Release event Identification
        /// </summary>
        protected uint releaseType;
        /// <summary>
        /// This contains the previous event type detected
        /// </summary>
        protected eventType previousEvent;
        /// <summary>
        /// The event to be send to the maneger
        /// </summary>
        LargeDisplayEvent evt;
        #endregion

        #region Public Properties
        /// <summary>
        /// Lesson title.
        /// </summary>
        public override string Title
        {
            get
            {
                return "LDF C#";
            }
        }

        /// <summary>
        /// Lesson description.
        /// </summary>
        public override string Description
        {
            get
            {
                return "This is a default example showing how to use LDF in C# ";
            }
        }

        /// <summary>
        /// Lesson URL.
        /// </summary>
        public override string Url
        {
            get
            {
                return "http://ktangao.wordpress.com";
            }
        }
        #endregion Public Properties

        #region Constructor
        /// <summary>
        /// Default constructor of this class
        /// </summary>
        public MainWindow()
            : base()
        {
            width = 800; height = 600;
            previousEvent = eventType.none;
            ConstantProvider constants = ConstantProvider.getInstance();
	        pressType = constants.getEventTypeIdentifier("PRESS");
	        dragType = constants.getEventTypeIdentifier("DRAG");
	        releaseType = constants.getEventTypeIdentifier("RELEASE");
            evt = new LargeDisplayEvent(0, 0, 0.0f, 0.0f, 0.0f);
        }
        #endregion

        #region Main()
        /// <summary>
        /// Application's entry point.  Runs this example.
        /// </summary>
        public static void Main()
        {
           // App.ShowCursorWindowed = false;
            App.ShowCursorFullscreen = false;
            App.RunWhileMinimized = false;
            App.Run(new MainWindow());
        }
        #endregion

        #region Initialize
        /// <summary>
        ///  Overrides OpenGL's initialization.
        /// </summary>
        public override void Initialize()
        {
            GL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            // OpenGL setup and optimization
            GL.glHint(GL.GL_POINT_SMOOTH_HINT, GL.GL_NICEST);
            GL.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
            GL.glEnable(GL.GL_BLEND);
            GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

            GL.glDisable(GL_DEPTH_TEST);        // we don't need depth buffering
            GL.glDisable(GL_CULL_FACE);         // we don't need backface culling right now
            GL.glDisable(GL_LIGHTING);          // we don't need lighting right now
            GL.glDisable(GL_POLYGON_SMOOTH);    // we don't need anti-aliasing right now
            GL.glDisable(GL_NORMALIZE);         // we don't use lights so we don't need normals
            GL.glDisable(GL_DITHER);            // we don't need dithering

            // The manager HAS to be created in here to have a GL context available!
            // If not, initializations that rely on OpenGL (e.g., display lists) are
            // not done properly.
            manager = new ApplicationManager(new WindowsFrameTimer(10));
        }
        #endregion

        #region Draw
        /// <summary>
        /// Draws the Scene
        /// </summary>
        public override void Draw()
        {
            GL.glClear(GL.GL_COLOR_BUFFER_BIT);
            manager.renderAll(false);
        }
        #endregion

        #region Reshape
        /// <summary>
        /// Overrides OpenGL reshaping.
        /// </summary>
        /// <param name="width">New width.</param>
        /// <param name="height">New Height</param>
        public override void Reshape(int width, int height)
        {
            this.width = (uint)width;
            this.height = (uint)height;
            GL.glViewport(0, 0, (int)width, (int)height);
            GL.glMatrixMode(GL.GL_PROJECTION);
            GL.glLoadIdentity();
            GL.glOrtho(0.0, (double)width, 0.0, (double)height, -1.0, 1.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            manager.resize(this.width, this.height);
        }
        #endregion

        #region InputHelp()
        /// <summary>
        /// Overrides default input help, supplying lesson-specific help information.
        /// </summary>
        public override void InputHelp()
        {
            base.InputHelp(); // Set Up The Default Input Help
        }
        #endregion InputHelp()

        #region ProcessEvents
        /// <summary>
        /// This function is called when an event occures...
        /// </summary>
        public override void ProcessInput()
        {
            base.ProcessInput();
            if (Mouse.LeftButton && ((previousEvent == eventType.none) || (previousEvent == eventType.release)))
                PressEvent();
            else if (Mouse.LeftButton && ((previousEvent == eventType.press)||(previousEvent == eventType.drag)))
                DragEvent();
            else if(!Mouse.LeftButton && ((previousEvent == eventType.press)||(previousEvent == eventType.drag)))
                ReleaseEvent();
        }
        #endregion

        #region Mouse eventHandling
        /// <summary>
        /// Propagate a Press Event
        /// detection
        /// </summary>
        protected void PressEvent()
        {
            uint LDFx = 0, LDFy = 0;
            int mouseScreenPositionX = 0, mouseScreenPositionY = 0;
            CsGL.Util.Mouse.GetPosition(out mouseScreenPositionX, out mouseScreenPositionY);
            convertScreenToLDFWindowCoordinate((uint)mouseScreenPositionX, (uint)mouseScreenPositionY, out LDFx, out LDFy);
            VisComponent component = manager.getComponentAt(LDFx, LDFy);
            evt.setAttribute(pressType, 0, (float)LDFx, (float)LDFy, 0.0f);
            //LargeDisplayEvent evt = new LargeDisplayEvent(
                //pressType, 0, (float)LDFx, (float)LDFy, 0.0f);
            if (component == null)
            {
                VisComponent touch = manager.getTouchIndicator(0);
                touch.onEvent(evt);
                return;
            }
		    component.moveNodeToFront();
		    manager.processEvent(evt, component);
            //evt = null;
            previousEvent = eventType.press;
        }
        /// <summary>
        /// Propagate a Drag Event
        /// detection
        /// </summary>
        protected void DragEvent()
        {
            uint LDFx = 0, LDFy = 0;
            int mouseScreenPositionX = 0, mouseScreenPositionY = 0;
            CsGL.Util.Mouse.GetPosition(out mouseScreenPositionX, out mouseScreenPositionY);
            convertScreenToLDFWindowCoordinate((uint)mouseScreenPositionX, (uint)mouseScreenPositionY, out LDFx, out LDFy);
            evt.setAttribute(dragType, 0, (float)LDFx, (float)LDFy, 0.0f);
            //LargeDisplayEvent evt = new LargeDisplayEvent(
		        //dragType, 0, (float)LDFx, (float)LDFy, 0.0f);
	        manager.processEvent(evt, null);
	        //evt = null;
            previousEvent = eventType.drag;
        }
        /// <summary>
        /// Propagate a Release Event
        /// detection
        /// </summary>
        protected void ReleaseEvent()
        {
            uint LDFx = 0, LDFy = 0;
            int mouseScreenPositionX = 0, mouseScreenPositionY = 0;
            CsGL.Util.Mouse.GetPosition(out mouseScreenPositionX, out mouseScreenPositionY);
            convertScreenToLDFWindowCoordinate((uint)mouseScreenPositionX, (uint)mouseScreenPositionY, out LDFx, out LDFy);
            VisComponent component = manager.getComponentAt(LDFx, LDFy);
            evt.setAttribute(releaseType, 0, (float)LDFx, (float)LDFy, 0.0f);
            //LargeDisplayEvent evt = new LargeDisplayEvent(
                //releaseType, 0, (float)LDFx, (float)LDFy, (float)0.0f);
            if (component == null)
            {
                VisComponent touch = manager.getTouchIndicator(0);
                touch.onEvent(evt);
                return;
            }
	        manager.addToUpdateList(component.Id);
	        manager.processEvent(evt, null);
            //evt = null;
            previousEvent = eventType.release;
        }
        #endregion

        #region Coordinate Conversion
        /// <summary>
        /// Convert a given point into ldf coordinate;
        /// the center of ldf coordinate is the lower left corner
        /// of the window
        /// </summary>
        /// <param name="screenX">The x-ScreeCoordinate of the point to be converted</param>
        /// <param name="screenY">The y-ScreenCoordinate of the poin to be converted</param>
        /// <param name="LDFx">The equivalent x-LdfCoordinate of the point</param>
        /// <param name="LDFy">The eauivalent y-LdfCoordinate of the point</param>
        public void convertScreenToLDFWindowCoordinate(uint screenX, uint screenY, out uint LDFx, out uint LDFy)
        {
            //get the position of the lower left point wich is ldf (0,0)
            uint ldfCenterScreenCoordinateX = (uint)App.Form.Location.X;
            uint ldfCenterScreenCoordinateY = (uint)(App.Form.Location.Y + App.Form.Height );
            //uint ldfCenterScreenCoordinateY = (uint)(App.Form.ClientRectangle.Y + App.Form.ClientRectangle.Height);

            LDFx = screenX - ldfCenterScreenCoordinateX;
            LDFy = ldfCenterScreenCoordinateY - screenY;
            //LDFy = ldfCenterScreenCoordinateY - screenY;
        }
        #endregion
    }
}
